using System;
using System.Collections;
using System.Xml;
using System.IO;
using System.Text;

namespace SoftEng
{

    public class ExternalInterfaceSerializer
    {
        public string functionName;
        public ArrayList arguments;
        private void WriteArray(XmlTextWriter writer, ArrayList array)
        {
            writer.WriteStartElement("array");

            int index = 0;

            foreach (Object value in array)
            {
                writer.WriteStartElement("property");
                writer.WriteAttributeString("id", index.ToString());
                WriteElement(writer, value);
                writer.WriteEndElement();
                index++;
            }

            writer.WriteEndElement();
        }
        private void WriteObject(XmlTextWriter writer, Hashtable table)
        {
            writer.WriteStartElement("object");

            foreach (DictionaryEntry entry in table)
            {
                writer.WriteStartElement("property");
                writer.WriteAttributeString("id", entry.Key.ToString());
                WriteElement(writer, entry.Value);
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }
        private void WriteElement(XmlTextWriter writer, Object value)
        {
            if (value == null)
            {
                writer.WriteStartElement("null");
                writer.WriteEndElement();
            }
            else if (value is string)
            {
                writer.WriteStartElement("string");
                writer.WriteString(value.ToString());
                writer.WriteEndElement();
            }
            else if (value is bool)
            {
                writer.WriteStartElement((bool)value ? "true" : "false");
                writer.WriteEndElement();
            }
            else if (value is Single || value is Double || value is int || value is uint)
            {
                writer.WriteStartElement("number");
                writer.WriteString(value.ToString());
                writer.WriteEndElement();
            }
            else if (value is ArrayList)
            {
                WriteArray(writer, (ArrayList)value);
            }
            else if (value is Hashtable)
            {
                WriteObject(writer, (Hashtable)value);
            }
            else
            {
                throw new Exception("Could not encode type of " + value);
            }
        }
        public string EncodeInvoke(string functionName, ArrayList arguments)
        {
            StringBuilder sb = new StringBuilder();

            XmlTextWriter writer = new XmlTextWriter(new StringWriter(sb));

            writer.WriteStartElement("invoke");
            writer.WriteAttributeString("name", functionName);
            writer.WriteAttributeString("returntype", "xml");

            writer.WriteStartElement("arguments");

            foreach (Object value in arguments)
            {
                WriteElement(writer, value);
            }

            writer.WriteEndElement();
            writer.WriteEndElement();

            writer.Flush();
            writer.Close();

            return sb.ToString();
        }

        public string EncodeResult(Object value)
        {
            StringBuilder sb = new StringBuilder();

            XmlTextWriter writer = new XmlTextWriter(new StringWriter(sb));

            WriteElement(writer, value);

            writer.Flush();
            writer.Close();

            return sb.ToString();
        }

        public void DecodeInvoke(string xml)
        {
            XmlTextReader reader = new XmlTextReader(xml, XmlNodeType.Document, null);

            reader.Read();

            functionName = reader.GetAttribute("name");
            arguments = new ArrayList();

            reader.ReadStartElement("invoke");
            reader.ReadStartElement("arguments");

            while (!(reader.NodeType == XmlNodeType.EndElement && reader.Name == "arguments"))
            {
                arguments.Add(ReadElement(reader));
            }

            reader.ReadEndElement();
            reader.ReadEndElement();
        }

        public Object DecodeResult(string xml)
        {
            XmlTextReader reader = new XmlTextReader(xml, XmlNodeType.Document, null);
            reader.Read();
            return ReadElement(reader);
        }

        private ArrayList ReadArray(XmlTextReader reader)
        {
            ArrayList array = new ArrayList();

            while (!(reader.NodeType == XmlNodeType.EndElement && reader.Name == "array"))
            {
                int id = int.Parse(reader.GetAttribute("id"));
                reader.ReadStartElement("property");
                array.Add(ReadElement(reader));
                reader.ReadEndElement();
            }

            return array;
        }

        private Hashtable ReadObject(XmlTextReader reader)
        {
            Hashtable table = new Hashtable();

            while (!(reader.NodeType == XmlNodeType.EndElement && reader.Name == "object"))
            {
                string id = reader.GetAttribute("id");
                reader.ReadStartElement("property");
                table.Add(id, ReadElement(reader));
                reader.ReadEndElement();
            }

            return table;
        }

        private Object ReadElement(XmlTextReader reader)
        {
            if (reader.NodeType != XmlNodeType.Element)
            {
                throw new XmlException();
            }

            if (reader.Name == "true")
            {
                reader.Read();
                return true;
            }

            if (reader.Name == "false")
            {
                reader.Read();
                return false;
            }

            if (reader.Name == "null" || reader.Name == "undefined")
            {
                reader.Read();
                return null;
            }

            if (reader.IsStartElement("number"))
            {
                reader.ReadStartElement("number");
                double value = Double.Parse(reader.Value);
                reader.Read();
                reader.ReadEndElement();
                return value;
            }

            if (reader.IsStartElement("string"))
            {
                reader.ReadStartElement("string");
                string value = reader.Value;
                reader.Read();
                reader.ReadEndElement();
                return value;
            }

            if (reader.IsStartElement("array"))
            {
                reader.ReadStartElement("array");
                ArrayList value = ReadArray(reader);
                reader.ReadEndElement();
                return value;
            }

            if (reader.IsStartElement("object"))
            {
                reader.ReadStartElement("object");
                Hashtable value = ReadObject(reader);
                reader.ReadEndElement();
                return value;
            }

            throw new XmlException();
        }

    }
}
